from repoze.who.plugins.auth_tkt import AuthTktCookiePlugin
from paste.request import get_cookies
from paste.auth import auth_tkt
from storm.zope.zstorm import global_zstorm
from storm.expr import SQL
import datetime
import time


from apkn.portal.users import User
from apkn.portal.session import Session

class StormAuthTktCookiePlugin(AuthTktCookiePlugin):

	def __init__(self, secret, cookie_name='auth_tkt',
				 secure=False, include_ip=False,
				 timeout=None, reissue_time=None, userid_checker=None,
				 store_name=None, store_uri=None):
		self.secret = secret
		self.cookie_name = cookie_name
		self.include_ip = include_ip
		self.secure = secure
		if timeout and ( (not reissue_time) or (reissue_time > timeout) ):
			raise ValueError('When timeout is specified, reissue_time must '
							 'be set to a lower value')
		self.timeout = timeout
		self.reissue_time = reissue_time
		self.userid_checker = userid_checker
		self.store_name = store_name
		self.store_uri = store_uri

		
	def get_user_email(self, userid):
		store = global_zstorm.get(self.store_name, self.store_uri)
		user = store.find(User, User.username == userid).one()				  
		return user.email		
		
	def get_db_session(self, cookie_val):
		store = global_zstorm.get(self.store_name, self.store_uri)
		session_val = store.find(Session, Session.xauth_cookie == unicode(cookie_val)).one() 
		if session_val is not None:
			return False
		else:
			return True				   
			

	# IIdentifier
	def identify(self, environ):
		cookies = get_cookies(environ)
		cookie = cookies.get(self.cookie_name)
		
		if cookie is None or not cookie.value:
			return None	

		if self.include_ip:
			remote_addr = environ['REMOTE_ADDR']
		else:
			remote_addr = '0.0.0.0'

		try:
			timestamp, userid, tokens, user_data = auth_tkt.parse_ticket(
				self.secret, cookie.value, remote_addr)
		except auth_tkt.BadTicket:
			return None

		if self.userid_checker and not self.userid_checker(userid):
			return None

		if self.timeout and ( (timestamp + self.timeout) < time.time() ):
			return None


		userid_typename = 'userid_type:'
		user_data_info = user_data.split('|')
		for datum in filter(None, user_data_info):
			if datum.startswith(userid_typename):
				userid_type = datum[len(userid_typename):]
				decoder = self.userid_type_decoders.get(userid_type)
				if decoder:
					userid = decoder(userid)
					
		if cookie is not None:			
			#update the session table
			store = global_zstorm.get(self.store_name, self.store_uri)
			user = store.find(User, User.username == userid).one() 
			if self.get_db_session(cookie.value):
				session_val = Session()
				session_val.xauth_cookie = unicode(cookie.value)
				session_val.user_email = unicode(self.get_user_email(userid))	  
				store.add(session_val)
							   
			
		environ['REMOTE_USER_TOKENS'] = tokens
		environ['REMOTE_USER_DATA'] = user_data
		environ['AUTH_TYPE'] = 'cookie'
		
		identity = {}
		identity['timestamp'] = timestamp
		identity['repoze.who.userid'] = userid
		identity['tokens'] = tokens
		identity['userdata'] = user_data
		return identity
		
	# IIdentifier
	def forget(self, environ, identity):
		# return a set of expires Set-Cookie headers
		return self._get_cookies(environ, '""')
	
	def add_metadata(self, environ, identity):
		user_id = identity.get('repoze.who.userid')
		user_email = self.get_user_email(user_id)
		identity['user_email'] = user_email
		
	
	# IIdentifier
	def remember(self, environ, identity):
		if self.include_ip:
			remote_addr = environ['REMOTE_ADDR']
		else:
			remote_addr = '0.0.0.0'

		cookies = get_cookies(environ)
		old_cookie = cookies.get(self.cookie_name)
		existing = cookies.get(self.cookie_name)
		old_cookie_value = getattr(existing, 'value', None)

		timestamp, userid, tokens, userdata = None, '', '', ''

		if old_cookie_value:
			try:
				timestamp,userid,tokens,userdata = auth_tkt.parse_ticket(
					self.secret, old_cookie_value, remote_addr)
			except auth_tkt.BadTicket:
				pass

		who_userid = identity['repoze.who.userid']
		who_tokens = identity.get('tokens', '')
		who_userdata = identity.get('userdata', '')
	
		encoding_data = self.userid_type_encoders.get(type(who_userid))
		if encoding_data:
			encoding, encoder = encoding_data
			who_userid = encoder(who_userid)
			who_userdata = 'userid_type:%s' % encoding
		
		if not isinstance(tokens, basestring):
			tokens = ','.join(tokens)
		if not isinstance(who_tokens, basestring):
			who_tokens = ','.join(who_tokens)
		old_data = (userid, tokens, userdata)
		new_data = (who_userid, who_tokens, who_userdata)

		if old_data != new_data or (self.reissue_time and
				( (timestamp + self.reissue_time) < time.time() )):
			ticket = auth_tkt.AuthTicket(
				self.secret,
				who_userid,
				remote_addr,
				tokens=who_tokens,
				user_data=who_userdata,
				cookie_name=self.cookie_name,
				secure=self.secure)
			new_cookie_value = ticket.cookie_value()
			
			cur_domain = environ.get('HTTP_HOST', environ.get('SERVER_NAME'))
			wild_domain = '.' + cur_domain
			if old_cookie_value != new_cookie_value:
				# return a set of Set-Cookie headers
				return self._get_cookies(environ, new_cookie_value)

	def __repr__(self):
		return '<%s %s>' % (self.__class__.__name__,
							id(self)) #pragma NO COVERAGE		
		
def _bool(value):
	if isinstance(value, basestring):
		return value.lower() in ('yes', 'true', '1')
	return value		
		
def make_plugin(secret=None,
				secretfile=None,
				cookie_name='auth_tkt',
				secure=False,
				include_ip=False,
				timeout=None,
				reissue_time=None,
				userid_checker=None,
				store_name=None,
				store_uri=None,
			   ):
	from repoze.who.utils import resolveDotted
	if (secret is None and secretfile is None):
		raise ValueError("One of 'secret' or 'secretfile' must not be None.")
	if (secret is not None and secretfile is not None):
		raise ValueError("Specify only one of 'secret' or 'secretfile'.")
	if secretfile:
		secretfile = os.path.abspath(os.path.expanduser(secretfile))
		if not os.path.exists(secretfile):
			raise ValueError("No such 'secretfile': %s" % secretfile)
		secret = open(secretfile).read().strip()
	if timeout:
		timeout = int(timeout)
	if reissue_time:
		reissue_time = int(reissue_time)
	if userid_checker is not None:
		userid_checker = resolveDotted(userid_checker)
	if store_name is None:  
		raise ValueError('store_name must be specified') 
	if store_uri is None:
		raise ValueError('store_uri must be specified')		 
	plugin = StormAuthTktCookiePlugin(secret,
								 cookie_name,
								 _bool(secure),
								 _bool(include_ip),
								 timeout,
								 reissue_time,
								 userid_checker,
								 store_name,
								 store_uri,
								 )
	return plugin
	
				   



